.. _глава_35_start:

Параметры функций
=================

.. index::
    параметры функции

В этой главе рассматриваются различные виды параметров функций.
Некоторые из понятий этой главы уже встречались ранее в книге.
Например, ключевое слово ref, которое мы рассматривали в главе "Цикл foreach" (:ref:`цикл foreach<глава_29_start>`)
делало реальные элементы доступными в цикле foreach, а не копии этих элементов.
Кроме того, мы рассмотрели ключевые слова const и immutable, а также
различия между типами значений и ссылочными типами в предыдущих главах.
Мы написали функции, которые выдавали результаты, используя свои
параметров. Например, следующая функция использует свои параметры в
вычислении:

.. code-block:: d

    double weightedAverage(double quizGrade, double finalGrade) {
        return quizGrade * 0.4 + finalGrade * 0.6;
    }

Эта функция рассчитывает среднюю оценку, отнимая 40 % от оценки за тест и
60% от итоговой оценки. Вот как ее можно использовать

.. code-block:: d

    int quizGrade = 76;
    int finalGrade = 80;
    writefln("Weighted average: %2.0f",weightedAverage(quizGrade, finalGrade));

.. index::
    параметры всегда копируются

Параметры всегда копируются
---------------------------

В приведенном выше коде две переменные передаются в качестве аргументов в функцию
weightedAverage(). Функция использует свои параметры. Этот факт может создать
ложное впечатление, что функция использует фактические переменные, которые были переданы
в качестве аргументов. На самом деле функция использует копии этих переменных.
Это различие важно, потому что изменение параметра изменяет только его
копию. Это видно на примере следующей функции, которая пытается изменить свой
параметр (т. е. создавая побочный эффект). Предположим, что следующая функция
написана для уменьшения энергии игрового персонажа:


.. code-block:: d

    void reduceEnergy(double energy)
    {
        energy /= 4;
    }

Вот программа, которая использует reduceEnergy():

.. code-block:: d

    import std.stdio;

    void reduceEnergy(double energy)
    {
        energy /= 4;
    }

    void main()
    {
        double energy = 100;
        reduceEnergy(energy);
        writeln("New energy: ", energy);
    }

Вывод

.. code-block:: console

    New energy: 100  не изменилась

Хотя reduceEnergy() уменьшает значение своего параметра до четверти от его
исходного значения, переменная energy в main() не изменяется. Причина этого
в том, что переменная energy в main() и параметр energy в функции
reduceEnergy() разделены; параметр является копией переменной в main().
Чтобы пронаблюдать это более внимательно, давайте вставим несколько вызовов writeln():

.. code-block:: d

    import std.stdio;

    void reduceEnergy(double energy)
    {
        writeln("Entered the function : ", energy);
        energy /= 4;
        writeln("Leaving the function : ", energy);
    }

    void main()
    {
        double energy = 100;
        writeln("Calling the function : ", energy);
        reduceEnergy(energy);
        writeln("Returned from the function: ", energy);
    }

Вывод

.. code-block:: console

    Calling the function : 100
    Entered the function : 100
    Leaving the function : 25 ← the parameter changes,
    Returned from the function: 100 ← the variable remains the same

Ссылочные переменные не копируются
----------------------------------

Даже параметры ссылочных типов, таких как срезы, ассоциативные массивы и переменные класса
переменные копируются в функции. Однако исходные переменные, на которые есть
(то есть элементы срезов, ассоциативных массивов и объекты классов) не копируются
не копируются. По сути, такие переменные передаются в функции как ссылки: параметр
параметр становится еще одной ссылкой на исходный объект. Это означает, что
модификация, произведенная через ссылку, изменяет и исходный объект.
Так как строка это ассив символов, это относится и к строкам:

.. code-block:: d

    import std.stdio;

    void makeFirstLetterDot(dchar[] str)
    {
        str[0] = '.';
    }

    void main()
    {
        dchar[] str = "abc"d.dup;
        makeFirstLetterDot(str);
        writeln(str);
    }

Изменение, внесенное в первый элемент параметра, влияет на фактический элемент
в main():

.. code-block:: console

    .bc

Однако исходные переменные slice и ассоциативного массива по-прежнему передаются по копии.
Это может привести к неожиданным и на первый взгляд непредсказуемым результатам, если только параметры
параметры не квалифицированы как ref.

**Удивительная семантика ссылок в срезах**

Как мы видели в главе "Слайсы и другие возможности массивов" (стр. 65), добавление
элементов в срез может прекратить совместное использование элементов. Очевидно, что после прекращения совместного использования
параметр slice, такой как str выше, не будет являться ссылкой на элементы переданной исходной переменной.
Например, элемент, добавляемый следующей функцией, не будет
не будет виден вызывающей стороне:

.. code-block:: d

    import std.stdio;

    void appendZero(int[] arr)
    {
        arr ~= 0;
        writefln("Inside appendZero() : %s", arr);
    }

    void main()
    {
        auto arr = [ 1, 2 ];
        appendZero(arr);
        writefln("After appendZero() returns: %s", arr);
    }

Элемент добавляется только к параметру функции, но не к исходному фрагменту:

.. code-block:: console

    Inside appendZero() : [1, 2, 0]
    After appendZero() returns: [1, 2] ← No 0

Если новые элементы должны быть добавлены к исходному фрагменту, то фрагмент должен быть
быть передан как ref:

.. code-block:: d

    void appendZero(ref int[] arr)
    {
    // ...
    }

О классификаторе ref будет рассказано ниже.

**Удивительная семантика ссылок в ассоциативных массивах**

Ассоциативные массивы, передаваемые в качестве параметров функции, также могут вызывать сюрпризы
потому что ассоциативные массивы начинают свою жизнь с null, а не с пустого массива
В данном контексте null означает неинициализированный ассоциативный массив. Ассоциативные
массивы инициализируются автоматически, когда в них добавляется первая пара ключ-значение. Как
как следствие, если функция добавляет элемент в нулевой ассоциативный массив, то этот элемент
элемент не будет виден в исходной переменной, потому что, хотя параметр
инициализируется, исходная переменная остается null:

.. code-block:: d

    import std.stdio;

    void appendElement(int[string] aa)
    {
        aa["red"] = 100;
        writefln("Inside appendElement() : %s", aa);
    }

    void main()
    {
        int[string] aa; // ← null to begin with
        appendElement(aa);
        writefln("After appendElement() returns: %s", aa);
    }

.. code-block:: d

    Inside appendElement() : ["red":100]
    After appendElement() returns: []  Still null

С другой стороны, если бы ассоциативный массив изначально не был нулевым, то
добавленный элемент также будет виден вызывающей стороне:

.. code-block:: d

    int[string] aa;
    aa["blue"] = 10; // ← Not null before the call
    appendElement(aa);

На этот раз добавленный элемент видит вызывающий абонент:

.. code-block:: console

    Inside appendElement() : ["red":100, "blue":10]
    After appendElement() returns: ["red":100, "blue":10]

По этой причине, возможно, лучше передавать ассоциативный массив в качестве параметра ref,
о чем будет рассказано ниже.

.. index::
    квалификаторы параметров

Квалификаторы параметров
------------------------

Параметры передаются в функции в соответствии с общими правилами, описанными
выше:

* Типы значений копируются, после чего исходная переменная и копия становятся
  независимыми.
* Ссылочные типы также копируются, но в силу природы ссылочных типов и исходная ссылка, и параметр предоставляют доступ к одной и той же переменной.
  оригинальная ссылка и параметр предоставляют доступ к одной и той же переменной.
  Это правила по умолчанию, которые применяются, когда определения параметров не имеют
  квалификаторов. Следующие квалификаторы изменяют способ передачи параметров и
  какие операции над ними разрешены.

.. index::
    in
    out
    const
    immutable

**in**

По умолчанию параметры in аналогичны параметрам const. Они не могут быть
изменены:

.. code-block:: d

    void foo(in int value)
    {
        value = 1; // ошибка компиляции
    }


Однако, когда используется переключатель командной строки компилятора -preview=in, параметры in
параметры становятся более полезными для выражения намерения "этот параметр используется
только в качестве входного параметра этой функции":

.. code-block:: console

    $ dmd -preview=in deneme.d


-preview=in изменяет значение параметров in и позволяет компилятору
выбирать более подходящие методы при передаче аргументов для параметров in:

* Значение in меняется на значение const scope (о scope см. ниже)
* В отличие от параметров ref, в качестве параметров in можно передавать даже rvalue (см.
  ниже о параметрах ref и в следующей главе о параметрах rvalues)
* Типы, которые при копировании могут вызвать побочный эффект (например, когда определен конструктор копирования
  конструктора) или типы, которые не могут быть скопированы (например, конструктор копирования
  отключен), передаются по ссылке

Я рекомендую использовать параметры in, а не const, независимо от того, используется ли ключ
-preview=in переключатель командной строки используется или нет.

**out**

Мы знаем, что функции возвращают то, что они производят, в качестве возвращаемых значений. Тот факт
что есть только одно возвращаемое значение, иногда ограничивает, поскольку некоторые функции могут
необходимо получить более одного результата. *(Примечание: Можно вернуть более одного
результат, определив тип возвращаемого значения как кортеж или структуру. Мы рассмотрим эти возможности в
последующих главах.)*
Ключевое слово out позволяет функциям возвращать результаты через свои
параметры. Когда параметры out изменяются внутри функции, эти
изменения затрагивают исходную переменную, которая была передана функции. В
В некотором смысле, присвоенное значение выходит из функции через параметр out.
Давайте рассмотрим функцию, которая делит два числа и выдает как
и остаток. Возвращаемое значение используется для вычисления коэффициента, а остаток
остаток возвращается через параметр out:

.. code-block:: d

    import std.stdio;

    int divide(int dividend, int divisor, out int remainder)
    {
        remainder = dividend % divisor;
        return dividend / divisor;
    }

    void main()
    {
        int remainder;
        int result = divide(7, 3, remainder);
        writeln("result: ", result, ", remainder: ", remainder);
    }


Даже если в функции нет явного присваивания параметру, значение параметра
значение параметра автоматически становится начальным значением int, влияя на
переменную в main():
Перед вызовом функции

.. code-block:: console

        Before calling the function : 100
        After entering the function : 0 ← the value of int.init
        After returning from the function: 0


Как видно из этого, параметры out не могут передавать значения в функции; они предназначены
предназначены только для передачи значений из функций.
В последующих главах мы увидим, что возврат типов Tuple или struct являются лучшей
альтернативой параметрам out.


**const**

Я рекомендую использовать параметры in, а не const.
Как мы видели ранее, const гарантирует, что параметр не будет изменен
внутри функции. Программистам полезно знать, что определенные
const также делает функции более полезными.
полезными, позволяя передавать const, неизменяемые и изменяемые переменные через
через этот параметр:

.. code-block:: d

    import std.stdio;

    dchar lastLetter(const dchar[] str)
    {
        return str[$ - 1];
    }

    void main()
    {
        writeln(lastLetter("constant"));
    }



**immutable**

Как мы видели ранее, неизменяемые функции требуют, чтобы определенные переменные
должны быть неизменяемыми. Из-за такого требования следующая функция может
может быть вызвана только для строк с неизменяемыми элементами (например, строковых литералов):

.. code-block:: d

    import std.stdio;

    dchar[] mix(immutable dchar[] first, immutable dchar[] second)
    {
        dchar[] result;
        int i;
        for (i = 0; (i < first.length) && (i < second.length); ++i)
        {
            result ~= first[i];
            result ~= second[i];
        }
        result ~= first[i .. $];
        result ~= second[i .. $];
        return result;
    }

    void main()
    {
        writeln(mix("HELLO", "world"));
    }


Поскольку это накладывает требования на параметр, неизменяемые параметры следует использовать
использовать только в тех случаях, когда требуется неизменяемость. В противном случае, в общем случае, const является более
полезнее, потому что он принимает неизменяемые, const и mutable переменные.

.. index::
    ref

**ref**

Это ключевое слово позволяет передавать переменную по ссылке, даже если она
обычно передается как копия (т. е. по значению).
rvalues (см. следующую главу) нельзя передавать в функции в качестве параметров ref.
Чтобы функция reduceEnergy(), которую мы видели ранее, изменила исходную переменную
переменную, она должна принимать свой параметр как ref:

.. code-block:: d

   import std.stdio;

    void reduceEnergy(ref double energy)
    {
        energy /= 4;
    }

    void main()
    {
        double energy = 100;
        reduceEnergy(energy);
        writeln("New energy: ", energy);
    }

На этот раз модификация параметра изменяет исходную
переменную в main():

.. code-block:: console

    New energy: 25

Как видно, параметры ref могут использоваться как в качестве входных, так и выходных.
параметры также можно рассматривать как псевдонимы исходных переменных. Приведенный на
параметр функции energy выше является псевдонимом переменной energy в main().
Подобно параметрам out, ref-параметры позволяют функциям иметь побочные эффекты
также. На самом деле функция reduceEnergy() не возвращает значения; она вызывает побочный
эффект через свой единственный параметр.
Стиль программирования, называемый функциональным программированием, отдает предпочтение возвращаемым значениям
а не побочным эффектам, настолько, что некоторые языки функционального программирования вообще не
не допускают побочных эффектов вообще. Это объясняется тем, что функции, которые выдают результаты исключительно
через возвращаемые значения, легче понять, реализовать и поддерживать.
Одна и та же функция может быть написана в стиле функционального программирования путем
возвращая результат, а не вызывая побочный эффект. Части программы, которые
изменились, выделены:

.. code-block:: d

    import std.stdio;

    double reducedEnergy(double energy)
    {
        return energy / 4;
    }

    void main()
    {
        double energy = 100;
        energy = reducedEnergy(energy);
        writeln("New energy: ", energy);
    }


Обратите внимание на изменение названия функции. Теперь это существительное, а не
глагола.

.. index::
    auto ref
    inout

**auto ref**

Этот классификатор можно использовать только с шаблонами (:ref:`Шаблоны<глава_64_start>`). Как мы увидим в
следующей главе, параметр auto ref принимает llvalues по ссылке, а rvalues - по копию.


**inout**

Несмотря на свое название, состоящее из in и out, это ключевое слово не означает вход и
выход; мы уже видели, что ввод и вывод осуществляются с помощью ключевого слова ref
ключевое слово.
inout переносит изменяемость параметра на возвращаемый тип. Если
параметр является const, immutable или mutable, то возвращаемое значение также будет const,
неизменяемым или изменяемым; соответственно.
Чтобы увидеть, как inout помогает в программах, давайте рассмотрим функцию, которая возвращает срез
внутренних элементов своего параметра:

.. code-block:: d

    import std.stdio;

    int[] inner(int[] slice)
    {
        if (slice.length)
        {
            --slice.length; // trim from the end
            if (slice.length)
            {
                slice = slice[1 .. $]; // trim from the beginning
            }
        }
        return slice;
    }

    void main()
    {
        int[] numbers = [5, 6, 7, 8, 9];
        writeln(inner(numbers));
    }


Вывод

.. code-block:: console

    [6, 7, 8]

Согласно тому, что мы установили до сих пор в книге, для того чтобы функция
функция была более полезной, ее параметр должен быть const(int)[], потому что
элементы не изменяются внутри функции. (Обратите внимание, что нет никакого вреда
в изменении самого фрагмента параметра, поскольку он является копией исходной переменной.)
Однако такое определение функции приведет к ошибке компиляции:

.. code-block:: d

    int[] inner(const(int)[] slice)
    {
    // ...
        return slice; //  ошибка компиляции
    }

Ошибка компиляции указывает на то, что фрагмент const(int) не может быть возвращен как
фрагмент мутабельного int:
.. code-block:: console

    Error: cannot implicitly convert expression (slice) of type const(int)[] to int[]

Можно подумать, что указание возвращаемого типа как const(int)[] будет правильным решением.
решением:

.. code-block:: d

    const(int)[] inner(const(int)[] slice)
    {
    // ...
        return slice; // now compiles
    }

Хотя код теперь компилируется, он вносит ограничение: даже когда функция вызывается
вызывается с фрагментом из изменяемых элементов, на этот раз возвращаемый срез оказывается
состоящий из const-элементов. Чтобы понять, насколько это ограничивает возможности, давайте посмотрим на
следующий код, который пытается изменить внутренние элементы среза:

.. code-block:: d

    int[] numbers = [ 5, 6, 7, 8, 9 ];
    int[] middle = inner(numbers); // ошибка компиляции
    middle[] *= 10;

Возвращаемый фрагмент типа const(int)[] не может быть присвоен фрагменту типа
int[], что приводит к ошибке:

.. code-block:: console

    Error: cannot implicitly convert expression (inner(numbers)) of type const(int)[] to int[]

Однако, поскольку мы начали с фрагмента с изменяемыми элементами, это ограничение является
искусственным и неудачным. inout решает эту проблему мутабельности между
параметрами и возвращаемыми значениями. Он указывается как на параметр, так и на
возвращаемого типа и переносит мутабельность первого на второе:

.. code-block:: d

    inout(int)[] inner(inout(int)[] slice)
    {
    // ...
        return slice;
    }

Благодаря этому изменению одну и ту же функцию теперь можно вызывать с const, immutable,
и мутабельными фрагментами:

.. code-block:: d

    {
        int[] numbers = [ 5, 6, 7, 8, 9 ];
        // The return type is a slice of mutable elements
        int[] middle = inner(numbers);
        middle[] *= 10;
        writeln(middle);
    }
    {
        immutable int[] numbers = [ 10, 11, 12 ];
        // The return type is a slice of immutable elements
        immutable int[] middle = inner(numbers);
        writeln(middle);
    }

    {
        const int[] numbers = [ 13, 14, 15, 16 ];
        // The return type is a slice of const elements
        const int[] middle = inner(numbers);
        writeln(middle);
    }

.. index::
    lazy

**lazy**

Естественно ожидать, что аргументы будут оцениваться перед входом в функции, которые
используют эти аргументы. Например, функция add(), приведенная ниже, вызывается с
возвращаемыми значениями двух других функций:

.. code-block:: d

    result = add(anAmount(), anotherAmount());

Чтобы вызвать функцию add(), сначала должны быть вызваны функции anAmount() и anotherAmount().
вызваны. В противном случае значения, которые нужны add(), будут недоступны.
Оценка аргументов перед вызовом функции называется предварительным выичслением.
Однако, в зависимости от определенных условий, некоторые параметры могут  вообще не получить шанса быть использованными в функции. В таких случаях вычисление аргументов
будет нерациональной.
Классический пример такой ситуации - функция протоколирования, которая выводит сообщение
только если важность сообщения выше определенного параметра конфигурации:

.. code-block:: d

    enum Level
    {
        low,
        medium,
        high
    }

    void log(Level level, string message)
    {
        if (level >= interestedLevel)
        {
            writeln(message);
        }

    }

Например, если пользователя интересуют только сообщения с Level.High, то
сообщение с уровнем Level.medium не будет напечатано. Однако аргумент
все равно будет вычислен перед вызовом функции. Например, все выражение format()  выражение ниже, включая вызов getConnectionState(), который оно выполняет, будет
будет потрачено впустую, если сообщение так и не будет напечатано:

.. code-block:: d

    if (failedToConnect)
    {
        log(Level.medium,format("Failure. The connection state is '%s'.",  getConnectionState()));
    }

Ключевое слово lazy указывает, что выражение, переданное в качестве параметра, будет
вычисляться только тогда, когда это необходимо:

.. code-block:: d

    void log(Level level, lazy string message)
    {
        // ... the body of the function is the same as before ...
    }

На этот раз выражение будет вычислено только в том случае, если используется параметр сообщения
используется.
Следует быть осторожным с тем, что ленивый параметр оценивается каждый раз, когда
когда этот параметр используется в функции.
Например, поскольку ленивый параметр следующей функции используется
три раза в функции, то выражение, которое дает его значение, вычисляется
три раза:


.. code-block:: d

    import std.stdio;

    int valueOfArgument()
    {
        writeln("Calculating...");
        return 1;
    }

    void functionWithLazyParameter(lazy int value)
    {
        int result = value + value + value;
        writeln(result);
    }

    void main()
    {
        functionWithLazyParameter(valueOfArgument());
    }

Вывод

.. code-block:: console

    Calculating
    Calculating
    Calculating
    3

**scope**

Это ключевое слово указывает, что параметр не будет использоваться за пределами области действия
функции. На момент написания статьи scope действует только в том случае, если функция определена как
@safe (стр. 547) и если используется ключ компилятора -dip1000. DIP - это сокращение от D
Предложение по улучшению. На момент написания статьи DIP 1000 является экспериментальной, поэтому она может работать не во всех случаях.

.. code-block:: console

    $ dmd -dip1000 deneme.d

.. code-block:: d

    int[] globalSlice;
    @safe int[] foo(scope int[] parameter)
    {
        globalSlice = parameter; // ошибка компиляции
        return parameter; // ошибка компиляции
    }

    void main()
    {
        int[] slice = [10, 20];
        int[] result = foo(slice);
    }


Приведенная выше функция нарушает обещание области видимости в двух местах: Она присваивает
глобальной переменной и возвращает параметр. Оба эти действия сделают
возможность доступа к параметру после завершения функции.

.. index::
    shared

**shared**

Это ключевое слово требует, чтобы параметр был доступен для совместного использования между потоками
выполнения:

.. code-block:: d

    void foo(shared int[] i)
    {
        // ...
    }

    void main()
    {
        int[] numbers = [10, 20];
        foo(numbers); // ошибка компиляции
    }


Приведенная выше программа не может быть скомпилирована, потому что аргумент не является общим.
Ниже приведены изменения, необходимые для компиляции:


.. code-block:: d

    shared int[] numbers = [ 10, 20 ];
    foo(numbers); // now compiles

Ключевое слово shared мы рассмотрим позже в главе Concurrency, посвященной совместному использованию данных
(:ref:`Совместное использование данных (Concurrency)<Совместное использование данных (Concurrency)>`).

**return**

Иногда функции полезно напрямую возвращать один из своих параметров ref.
Например, следующая функция pick() выбирает и возвращает один из своих
параметров случайным образом, чтобы вызывающий мог напрямую изменить тот, которому повезло:

.. code-block:: d

    import std.stdio;
    import std.random;

    ref int pick(ref int lhs, ref int rhs)
    {
        return uniform(0, 2) ? lhs : rhs; // ошибка компиляции
    }

    void main()
    {
        int a;
        int b;
        pick(a, b) = 42;
        writefln("a: %s, b: %s", a, b);
    }

В результате либо a, либо b внутри main() присваивается значение 42:

.. code-block:: console

    a: 42, b: 0
    a: 0, b: 42

К сожалению, один из аргументов функции pick() может иметь меньшее время жизни, чем
возвращаемой ссылки. Например, следующая функция foo() вызывает pick()
с двумя локальными переменными, фактически сама возвращая ссылку на одну из них:

.. code-block:: d

    import std.random;

    ref int pick(ref int lhs, ref int rhs)
    {
        return uniform(0, 2) ? lhs : rhs; // ← compilation ERROR TODO Should be clarified
    }

    ref int foo()
    {
        int a;
        int b;
        return pick(a, b); // ← BUG: возврат невалидной ссылки
    }

    void main()
    {
        foo() = 42; // ← BUG: запись по невалидному адресу
    }


Поскольку время жизни a и b заканчивается после выхода из foo(), присваивание в
main() не может быть выполнено действительной переменной. Это приводит к неопределенному поведению
программа не определяется спецификацией языка программирования. Ничего нельзя
нельзя сказать о поведении программы, которая содержит неопределенное поведение. (На
практике, однако, в приведенной выше программе значение 42, скорее всего, будет записано
в ячейку памяти, которую раньше занимали либо a, либо b.
в настоящее время часть несвязанной переменной, эффективно повреждая значение этой
несвязанной переменной).
Для предотвращения подобных ошибок к параметру можно применить ключевое слово return. Оно
указывает, что параметр должен быть ссылкой на переменную с большим временем жизни
чем возвращаемая ссылка:

.. code-block:: d

    import std.random;

    ref int pick(return ref int lhs, return ref int rhs)
    {
        return uniform(0, 2) ? lhs : rhs;
    }

    ref int foo()
    {
        int a;
        int b;
        return pick(a, b); // ← TODO compilation ERROR
    }

    void main()
    {
        foo() = 42;
    }

На этот раз компилятор видит, что аргументы pick() имеют меньшее время жизни
чем у ссылки, которую пытается вернуть foo():

.. code-block:: console

    Error: escaping reference to local variable a
    Error: escaping reference to local variable b

Эта возможность называется sealed references.
*Замечание: Хотя можно предположить, что компилятор может проверить pick() и обнаружить
ошибку даже без ключевого слова return, он не может сделать это в общем случае, поскольку тела
некоторых функций могут быть недоступны компилятору во время каждой компиляции.*

Итоги
-------

* Параметр - это то, что функция принимает от вызывающего ее пользователя для выполнения своей задачи.
* Аргумент - это выражение (например, переменная), которое передается функции в качестве
  параметр.
* По умолчанию каждый аргумент передается в виде копии. (Обратите внимание, что для ссылочных типов
  копируется именно ссылка, а не исходная переменная).
* in указывает, что параметр используется только для ввода данных. Предпочтительнее использовать in, чем
  const.
* out указывает, что параметр используется только для вывода данных.
* ref указывает, что параметр используется для ввода и вывода данных.
* auto ref используется только в шаблонах. Он указывает, что если аргумент является
  lvalue, то передается ссылка на него; если аргумент является rvalue, то он передается
  передается копией.
* const гарантирует, что параметр не будет изменен внутри функции.
  (Помните, что const является транзитивной: любые данные, достигаемые через переменную const
  переменной, также является const). Предпочтительнее in, чем const.
  immutable требует, чтобы аргумент был неизменяемым.
* inout появляется как у параметра, так и у возвращаемого типа, и передает
  мутабельность параметра в возвращаемый тип.
* lazy используется для того, чтобы параметр оценивался, когда (и каждый раз) он
  когда (и каждый раз) он действительно используется.
* scope гарантирует, что ни одна ссылка на параметр не будет утечка из
  функции.
* shared требует, чтобы параметр был общим.
* return on a parameter требует, чтобы параметр жил дольше, чем
  возвращаемой ссылки.

Упражнение
----------

Следующая программа пытается поменять местами значения двух аргументов:

.. code-block:: d

    import std.stdio;

    void swap(int first, int second)
    {
        int temp = first;
        first = second;
        second = temp;
    }

    void main()
    {
        int a = 1;
        int b = 2;
        swap(a, b);
        writeln(a, ' ', b);
    }

Однако программа не оказывает никакого влияния на a или b:
1 2 ← не поменялись местами
Исправьте функцию так, чтобы значения a и b поменялись местами.
:ref:`решения Параметры функций<Параметры функций решения>`
